#include "FanBool.h"

#include <string.h>
#include <fan-llvm/core/Core.h>
#include "ParseErr.h"
#include "Str.h"
#include "Type.h"
#include "UnsupportedErr.h"


static Str* kTrueStr;
static Str* kFalseStr;

Type* Bool_classType = NULL;
VTable* Bool_classVTable = NULL;


/**********************************************
 *
 * Methods and fields
 *
 **********************************************/


/* Bool equals(Obj? obj) */
Bool Bool_equals(Obj self, NullableObj obj)
{
  if (!VAL_IS_NULL(obj) && Obj_typeof(obj) == Bool_classType)
    return (VAL_GET_BOOL(self) == VAL_GET_BOOL(obj));
  else
    return FALSE;
}


/* Int hash() */
Int Bool_hash(Obj self)
{
  return VAL_GET_BOOL(self) ? 1231 : 1237;
}


/* Str toStr() */
Str* Bool_toStr(Obj self)
{
  return VAL_GET_BOOL(self) ? kTrueStr : kFalseStr;
}


/* Bool defVal */
Bool Bool_defVal(void)
{
  return FALSE;
}


/* Bool? fromStr(Str s, Bool checked) */
NullableBool Bool_fromStr_1(Str* s)
{
  return Bool_fromStr(s, TRUE);
}


/* Bool? fromStr(Str s, Bool checked) */
NullableBool Bool_fromStr(Str* s, Bool checked)
{
  if (Str_compare(VAL_MAKE_REF(s), VAL_MAKE_REF(kTrueStr)) == 0)
    return VAL_TRUE;
  if (Str_compare(VAL_MAKE_REF(s), VAL_MAKE_REF(kFalseStr)) == 0)
    return VAL_FALSE;

  if (checked)
    exceptionThrow(ParseErr_newWithFormattedMsg("Invalid Bool: '%r'", s));

  return VAL_NULL;
}


/* Str toLocale() */
Str* Bool_toLocale(Bool self)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Bool.toLocale not implemented yet"));
  return NULL;
}


/* Bool not() */
Bool Bool_not(Bool self)
{
  return !self;
}


/* Bool and(Bool b) */
Bool Bool_and(Bool self, Bool b)
{
  return self && b;
}


/* Bool or(Bool b) */
Bool Bool_or(Bool self, Bool b)
{
  return self || b;
}


/* Bool xor(Bool b) */
Bool Bool_xor(Bool self, Bool b)
{
  return self ^ b;
}


/* Str toCode() */
Str* Bool_toCode(Bool self)
{
  return self ? kTrueStr : kFalseStr;
}


/**********************************************
 *
 * Type-system functions
 *
 **********************************************/


Type* Bool_initType(void)
{
  if (Bool_classType == NULL)
  {
    Type* baseType = Obj_initType();
    VTable* vtable = Bool_initVTable();
    Type* type = Type_new("Bool", baseType, vtable, sizeof(FanBool), sizeof(FanBoolVTable));
    Type_registerMethodImplementation(type, "and", Bool_and, 1);
    Type_registerMethodImplementation(type, "equals", Bool_equals, 1);
    Type_registerMethodImplementation(type, "fromStr", Bool_fromStr_1, 1);
    Type_registerMethodImplementation(type, "fromStr", Bool_fromStr, 2);
    Type_registerMethodImplementation(type, "hash", Bool_hash, 0);
    Type_registerMethodImplementation(type, "not", Bool_not, 0);
    Type_registerMethodImplementation(type, "or", Bool_or, 1);
    Type_registerMethodImplementation(type, "toCode", Bool_toCode, 0);
    Type_registerMethodImplementation(type, "toLocale", Bool_toLocale, 0);
    Type_registerMethodImplementation(type, "toStr", Bool_toStr, 0);
    Type_registerMethodImplementation(type, "xor", Bool_xor, 1);
    Type_registerFieldImplementation(type, "defVal", Bool_defVal, NULL);
    Bool_classType = type;
    Bool_initStatic();
  }
  return Bool_classType;
}


VTable* Bool_initVTable(void)
{
  if (Bool_classVTable == NULL)
  {
    VTable* vtable = memoryAlloc(sizeof(FanBoolVTable));
    memcpy(vtable, Obj_initVTable(), sizeof(FanObjVTable));
    ((FanObjVTable*)vtable)->equals = Bool_equals;
    ((FanObjVTable*)vtable)->hash = Bool_hash;
    ((FanObjVTable*)vtable)->toStr = Bool_toStr;
    Bool_classVTable = vtable;
  }
  return Bool_classVTable;
}


void Bool_initStatic(void)
{
  kTrueStr = Str_newFromCStr("true");
  kFalseStr = Str_newFromCStr("false");
}
